using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using BanZuiCloudControlAgent.Utils.Logging;

namespace BanZuiCloudControlAgent.Utils.Common
{
    /// <summary>
    /// 通用缓存管理器，支持对各种数据类型进行缓存，并可设置过期时间
    /// </summary>
    public class CacheManager
    {
        private static readonly Logger _logger = LogManager.GetLogger(nameof(CacheManager));
        private static readonly ConcurrentDictionary<string, CacheItem> _cache = new ConcurrentDictionary<string, CacheItem>();
        private static readonly Timer _cleanupTimer;
        
        /// <summary>
        /// 静态构造函数，初始化清理过期缓存的定时器
        /// </summary>
        static CacheManager()
        {
            // 创建定时器，每60秒清理一次过期缓存
            _cleanupTimer = new Timer(CleanupExpiredCache, null, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(60));
            _logger.Info("缓存管理器初始化完成，已设置定时清理任务");
        }

        /// <summary>
        /// 清理过期的缓存项
        /// </summary>
        private static void CleanupExpiredCache(object state)
        {
            try
            {
                int removedCount = 0;
                foreach (var key in _cache.Keys)
                {
                    if (_cache.TryGetValue(key, out CacheItem item) && item.IsExpired())
                    {
                        if (_cache.TryRemove(key, out _))
                        {
                            removedCount++;
                        }
                    }
                }
                
                if (removedCount > 0)
                {
                    _logger.Debug($"缓存清理完成，已移除 {removedCount} 个过期缓存项");
                }
            }
            catch (Exception ex)
            {
                _logger.Error($"清理过期缓存时发生异常: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 添加或更新缓存项
        /// </summary>
        /// <typeparam name="T">缓存项的数据类型</typeparam>
        /// <param name="key">缓存键</param>
        /// <param name="value">缓存值</param>
        /// <param name="expirationTime">过期时间（默认10分钟）</param>
        public static void Set<T>(string key, T value, TimeSpan? expirationTime = null)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException(nameof(key), "缓存键不能为空");
            }

            var expiration = expirationTime ?? TimeSpan.FromMinutes(10);
            var expiryTime = DateTime.Now.Add(expiration);
            
            var cacheItem = new CacheItem
            {
                Value = value,
                ExpiryTime = expiryTime
            };

            _cache.AddOrUpdate(key, cacheItem, (k, oldValue) => cacheItem);
            _logger.Debug($"已添加/更新缓存项 [{key}]，过期时间: {expiryTime}");
        }

        /// <summary>
        /// 获取缓存项
        /// </summary>
        /// <typeparam name="T">缓存项的数据类型</typeparam>
        /// <param name="key">缓存键</param>
        /// <param name="value">输出参数，获取到的缓存值</param>
        /// <returns>如果缓存存在且未过期则返回true，否则返回false</returns>
        public static bool TryGet<T>(string key, out T value)
        {
            value = default;
            
            if (string.IsNullOrEmpty(key))
            {
                return false;
            }

            if (_cache.TryGetValue(key, out CacheItem item) && !item.IsExpired())
            {
                if (item.Value is T typedValue)
                {
                    value = typedValue;
                    return true;
                }
            }
            
            return false;
        }

        /// <summary>
        /// 获取缓存项，如果不存在或已过期，则使用指定的函数获取新值并缓存
        /// </summary>
        /// <typeparam name="T">缓存项的数据类型</typeparam>
        /// <param name="key">缓存键</param>
        /// <param name="valueFactory">获取新值的函数</param>
        /// <param name="expirationTime">过期时间（默认10分钟）</param>
        /// <returns>缓存值或新获取的值</returns>
        public static T GetOrAdd<T>(string key, Func<T> valueFactory, TimeSpan? expirationTime = null)
        {
            if (TryGet<T>(key, out T value))
            {
                return value;
            }

            // 缓存不存在或已过期，获取新值
            value = valueFactory();
            Set(key, value, expirationTime);
            return value;
        }

        /// <summary>
        /// 获取缓存项，如果不存在或已过期，则使用指定的异步函数获取新值并缓存
        /// </summary>
        /// <typeparam name="T">缓存项的数据类型</typeparam>
        /// <param name="key">缓存键</param>
        /// <param name="valueFactory">获取新值的异步函数</param>
        /// <param name="expirationTime">过期时间（默认10分钟）</param>
        /// <returns>缓存值或新获取的值</returns>
        public static T GetOrAddAsync<T>(string key, Func<T> valueFactory, TimeSpan? expirationTime = null)
        {
            return GetOrAdd(key, valueFactory, expirationTime);
        }

        /// <summary>
        /// 移除缓存项
        /// </summary>
        /// <param name="key">缓存键</param>
        /// <returns>如果成功移除则返回true，否则返回false</returns>
        public static bool Remove(string key)
        {
            if (string.IsNullOrEmpty(key))
            {
                return false;
            }

            bool result = _cache.TryRemove(key, out _);
            if (result)
            {
                _logger.Debug($"已移除缓存项 [{key}]");
            }
            return result;
        }

        /// <summary>
        /// 清空所有缓存
        /// </summary>
        public static void Clear()
        {
            _cache.Clear();
            _logger.Info("已清空所有缓存项");
        }

        /// <summary>
        /// 获取当前缓存项数量
        /// </summary>
        /// <returns>缓存项数量</returns>
        public static int Count()
        {
            return _cache.Count;
        }

        /// <summary>
        /// 缓存项类，包含值和过期时间
        /// </summary>
        private class CacheItem
        {
            /// <summary>
            /// 缓存的值
            /// </summary>
            public object Value { get; set; }
            
            /// <summary>
            /// 过期时间
            /// </summary>
            public DateTime ExpiryTime { get; set; }
            
            /// <summary>
            /// 检查缓存项是否已过期
            /// </summary>
            /// <returns>如果已过期则返回true，否则返回false</returns>
            public bool IsExpired()
            {
                return DateTime.Now >= ExpiryTime;
            }
        }
    }
} 